/**
 * Copyright (c) 2012, Ken Anderson.  All rights reserved.
 * Copyright (c) 2004-2006, Martin Fiedler.  All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright notice, 
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice, 
 *    this list of conditions and the following disclaimer in the documentation 
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 **/

#include <stdio.h>

#include "mcl_h264_decoder.h"
#include "h264.h"

using namespace mcl;

#define DISPLAY_COLOR

////////////////////////////////////////////
/////// Internal Functions /////////////////
////////////////////////////////////////////
// These functions are hidden so that 
// we don't expose the access to the underlying H264 decode library.

// Shift and clamp between 0 and 255.
int clamp(int i)
{
	i>>=20;
	if(i<0)i=0; 
	if(i>255)i=255;
	return i;
}

// Convert from YCrCb to RGBA
// Returns conversion status
DecodeStatus convert( const __frame &frame, mcl::Image &dest )
{
	DecodeStatus status = DecodeStatus_OK;
	if( dest.init( frame.Lwidth, frame.Lheight) == false )
	{
		status = DecodeStatus_ConversionFailure;
	}

	if( status == DecodeStatus_OK )
	{
		for(unsigned int row=0; row<dest.getHeight(); row++)
		{
			for(unsigned int col=0; col<dest.getWidth(); col++)
			{
				const unsigned int index = (row*frame.Lpitch+col);
				const unsigned int cIndex = row/2*frame.Cpitch+col/2;
#ifdef DISPLAY_COLOR
				const int  Y = (frame.L[index]-16)   *1225732;
				const int Pb = (frame.C[0][cIndex]-128)*1170;
				const int Pr = (frame.C[1][cIndex]-128)*1170;
				const int  R = clamp(Y        +1436*Pr);
				const int  G = clamp(Y- 352*Pb -731*Pr);
				const int  B = clamp(Y+1815*Pb);
#else
				const int  R = frame.L[index];
				const int  G = frame.L[index];
				const int  B = frame.L[index];
#endif

				dest.data()[index*4  ] = R;
				dest.data()[index*4+1] = G;
				dest.data()[index*4+2] = B;
				dest.data()[index*4+3] = 0xFF;
			}
		}
	}

	return status;
}

///////////////////////////////////////////
///////////// H264Decoder /////////////////
///////////////////////////////////////////

H264Decoder::H264Decoder() 
	: m_status(DecodeStatus_NotLoaded) 
{
}

H264Decoder::~H264Decoder()
{
	printf("\nDecoding done.\n");
	h264_close();
}
	
DecodeStatus H264Decoder::loadFile(const char* filename)
{
	m_status = DecodeStatus_NotLoaded;
	int info=h264_open(filename);
	if( info )
	{
		int width=H264_WIDTH(info);
		int height=H264_HEIGHT(info);
		if( m_image.init(width, height) == DecodeStatus_NotLoaded )
		{
			m_status = DecodeStatus_OK;
		}
	}

	return m_status;
}

DecodeStatus H264Decoder::decodeAndLoop()
{
	if( m_status == DecodeStatus_OK )
	{
		frame *f;
		f=h264_decode_frame(1);
		if(!f) {
			h264_rewind();
			f=h264_decode_frame(1);
			if(!f)
			{
				m_status = DecodeStatus_DecodeFailure;
			}
		}
		if( m_status == DecodeStatus_OK )
		{
			m_status = convert( *f, m_image );
		}
	}
	return m_status;
}
